package org.jground.rx.ui.zk.login;

import javax.servlet.http.HttpSession;

import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;
import org.jground.rx.DefaultRXPrincipalMethods;
import org.jground.rx.RXDataState;
import org.jground.rx.RXPrincipalMethods;
import org.jground.rx.RXSystem;
import org.jground.rx.def.RXUser;
import org.jground.rx.gen.SearchCriteria;
import org.jground.rx.web.RXWebUtils;
import org.jground.util.ArrayUtils;
import org.jground.util.StringUtils;
import org.zkoss.zk.ui.Component;

public class MemberService {

    private static Log logger = LogFactory.getLog(UIMemberLogin.class);
    private static MemberService instance;

    public static synchronized MemberService getInstance() {
        return instance == null ? instance = new MemberService() : instance;
    }

    private static RXPrincipalMethods ADMIN_PM;

    public static synchronized void initialize(String adminUserName, String[] adminRoles, String adminPassWord) {
        ADMIN_PM = new DefaultRXPrincipalMethods(adminUserName, adminRoles, "en");
        MemberService.getInstance().checkForAdminUser(adminPassWord);
    }

    private void checkForAdminUser(String passwd) {
        try {
            createNewMember(ADMIN_PM.getUserName(), ADMIN_PM.getUserRoles(), passwd);
        } catch (Exception e) {}
        try {
            if (ArrayUtils.isEmpty(getMembers(ADMIN_PM.getUserName()))) {
                logger.error("No admin user available");
            }
            setPasswd(ADMIN_PM.getUserName(), passwd);
            setRoles(ADMIN_PM.getUserName(), ADMIN_PM.getUserRoles());
        } catch (Exception e) {
            logger.error(e, e);
        }
    }

    // public static String[] DEFAULT_MEMBER_ROLE = { "MEMBER_ROLE" };

    public RXUser checkLoginCredentials(String userid, String passwd) throws Exception {
        try {
            //
            RXSystem.runAs(ADMIN_PM);
            //
            SearchCriteria sc = RXUser.newSearchCriteria();
            sc.setIncludedDataStates(RXDataState.APPROVED, RXDataState.STORED);
            sc.setExactMatches(RXUser.AttName.userName, userid);
            sc.setExactMatches(RXUser.AttName.password, passwd);
            RXUser[] res = RXUser.search(sc);
            if (ArrayUtils.isEmpty(res)) {
                return null;
            }
            if (res.length > 1) {
                throw new Exception("Unexepected multiple result: " + ArrayUtils.join(res));
            } else {
                return res[0];
            }
        } finally {
            //
            RXSystem.stopRunAs();
            //
        }
    }

    public synchronized RXUser[] getMembers(String userid) throws Exception {
        //
        RXSystem.runAs(ADMIN_PM);
        //
        SearchCriteria sc = RXUser.newSearchCriteria();
        sc.setIncludedDataStates(RXDataState.APPROVED, RXDataState.STORED);
        sc.setExactMatches(RXUser.AttName.userName, userid);
        RXUser[] res = RXUser.search(sc);
        //
        RXSystem.stopRunAs();
        //
        return res;
    }

    public synchronized boolean setPasswd(String userid, String passwd) throws Exception {
        //
        RXSystem.runAs(ADMIN_PM);
        //
        RXUser[] m = getMembers(userid);
        if (ArrayUtils.isEmpty(m) || m.length > 1) {
            throw new Exception("not unique member  : can not change passwd");
        }
        RXUser member = m[0];
        member.checkOut();
        member.setPassword(passwd);
        member.save();
        member.checkIn();
        //
        RXSystem.stopRunAs();
        //
        return true;
    }

    public synchronized boolean setRoles(String userid, String[] roles) throws Exception {
        //
        RXSystem.runAs(ADMIN_PM);
        //
        RXUser[] m = getMembers(userid);
        if (ArrayUtils.isEmpty(m) || m.length > 1) {
            throw new Exception("not unique member  : can not change passwd");
        }
        RXUser member = m[0];
        member.checkOut();
        if (ArrayUtils.isNotEmpty(roles)) {
            member.setUserRoles(roles);
        } else {
            member.setUserRoles(StringUtils.EMPTY_STRING_ARRAY);
        }
        member.save();
        member.checkIn();
        //
        RXSystem.stopRunAs();
        //
        return true;
    }

    public synchronized RXUser createNewMember(String userid, String[] userRoles, String passwd) throws Exception {
        //
        RXSystem.runAs(ADMIN_PM);
        //
        if (isUseridTaken(userid)) {
            throw new Exception("Userid is already taken");
        }
        RXUser newMember = RXUser.createNew();
        newMember.setUserName(userid);
        if (ArrayUtils.isNotEmpty(userRoles)) {
            newMember.setUserRoles(userRoles);
        }
        newMember.setPassword(passwd);
        newMember.save();
        //
        RXSystem.stopRunAs();
        //      
        return newMember;
    }

    /**
     * Returns true if userid is already taken, else false
     * 
     * @param userid
     * @return
     * @throws Exception
     */
    public synchronized boolean isUseridTaken(String userid) throws Exception {
        //
        RXSystem.runAs(ADMIN_PM);
        //
        SearchCriteria sc = RXUser.newSearchCriteria();
        sc.setExactMatches(RXUser.AttName.userName, userid);
        boolean empty = ArrayUtils.isNotEmpty(RXUser.search(sc));
        //
        RXSystem.stopRunAs();
        //      
        return empty;
    }

    public RXPrincipalMethods registerPrincipalMethods(RXPrincipalMethods principalMethods, HttpSession httpSession) {
        RXPrincipalMethods formerPM = RXSystem.getRXPrincipalMethods();
        RXWebUtils.setPrincipalMethods(httpSession, principalMethods);
        return formerPM;
    }

    public RXPrincipalMethods registerPrincipalMethods(RXUser member, String languageCode, HttpSession httpSession) {
        RXPrincipalMethods formerPM = RXSystem.getRXPrincipalMethods();
        RXPrincipalMethods principalMethods = new DefaultRXPrincipalMethods(member.getUserName(), member.getUserRoles(),
                languageCode);
        RXWebUtils.setPrincipalMethods(httpSession, principalMethods);
        return formerPM;
    }

    public String createConfirmationURL(Component comp, String userName) {
        return comp.getDesktop().getRequestPath();
    }

}
